# -*- coding: utf-8 -*-
# @Time    : 2024/4/22 16:30
# @Author  : Jonny
# @File    : LuckMain.py
# @Describe: 告警入口

from flask import Flask, request, jsonify, g, send_from_directory, Response
from flask_cors import CORS
from flask_basicauth import BasicAuth
import json
import os
from logging.handlers import TimedRotatingFileHandler
import logging
from datetime import datetime
from urllib.parse import parse_qs

from luck.core.LuckConstant import LuckConstant
from luck.util.LuckLogHelperUtils import LuckLogHelperUtils
from luck.util.LuckDbInitHelperUtils import LuckDbInitHelperUtils

# 导入地图模块蓝图
from luck.map.sdk.baidu.controller.MapSdkBaiduController import baidu_map_bp
from luck.map.sdk.gaode.controller.MapSdkGaodeController import gaode_map_bp
# 导入费用计算模块蓝图
from luck.map.cost.controller.MapCostController import cost_bp

# 创建Flask应用实例
# 获取当前脚本的路径
script_dir = os.path.dirname(os.path.abspath(__file__))  # 项目工程路径/luck
static_folder = os.path.join(script_dir, 'static')
app = Flask(__name__, static_folder=static_folder, static_url_path='/static')
# 设置session密钥
app.secret_key = 'luck_sms_secret_key_2024'
# 允许所有域名进行跨域请求
CORS(app, resources={r"/*": {"origins": "*"}}, allow_headers=[LuckConstant.ACCESS_TOKEN_OLD,LuckConstant.ACCESS_TOKEN])

# 解决中文乱码问题
app.config['JSON_AS_ASCII'] = False

# 如果works/logs文件夹不存在则创建
if not os.path.exists(os.path.join(script_dir, LuckConstant.LOG_DIR)):
    os.makedirs(os.path.join(script_dir, LuckConstant.LOG_DIR))

app.config['LOG_FILE'] = os.path.join(script_dir, LuckConstant.LOG_DIR, LuckConstant.LOG_FILE)  # 修改log文件路径到logs文件夹下
app.config['LOG_LEVEL'] = logging.INFO
app.config['LOG_FORMAT'] = '%(asctime)s %(levelname)s: %(message)s'

# 配置全局日志
LOG_FILE = app.config['LOG_FILE']
LOG_LEVEL = app.config['LOG_LEVEL']
LOG_FORMAT = app.config['LOG_FORMAT']

# 设置日志格式
formatter = logging.Formatter(LOG_FORMAT)

# 设置日志处理器，按天生成日志文件
handler = TimedRotatingFileHandler(LOG_FILE, when="midnight", interval=1, backupCount=3)  # 每天生成一个新的日志文件，保留最近3天的日志文件
handler.setFormatter(formatter)  # 设置日志格式
handler.setLevel(LOG_LEVEL)  # 设置日志级别

# 创建并配置logger对象
logger = logging.getLogger()  # 获取root logger对象
logger.setLevel(LOG_LEVEL)  # 设置root logger的日志级别
logger.addHandler(handler)  # 添加handler到root logger对象中

# 创建一个handler，用于写入日志到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)  # 也可以为handler单独设置级别

# 创建一个日志格式器，并将其添加到handler中
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

# 将handler添加到logger中
logger.addHandler(console_handler)
logger.info('Start of the program')  # 输出一条info级别的日志，用于测试日志功能是否正常开启

# 初始化配置变量
config = {}

# 从环境变量中读取参数
server_port = os.getenv("SERVER_PORT")
if server_port is not None and server_port != '':
    config = {
        "server_port": int(server_port),

        "log_app_key": os.getenv("LOG_APP_KEY"),
        "log_app_secret": os.getenv("LOG_APP_SECRET"),
        "log_url": os.getenv("LOG_URL"),
        "log_url_basic_auth1": os.getenv("LOG_URL_BASIC_AUTH1"),
        "log_url_basic_auth2": os.getenv("LOG_URL_BASIC_AUTH2")
    }
    # 认证鉴权
    basic_auth1 = os.getenv("BASIC_AUTH1")
    if basic_auth1 is None or len(basic_auth1) <= 0:
        basic_auth1 = "admin"
    config["basic_auth1"] = basic_auth1

    basic_auth2 = os.getenv("BASIC_AUTH2")
    if basic_auth2 is None or len(basic_auth2) <= 0:
        basic_auth2 = "luckserver"
    config["basic_auth2"] = basic_auth2
else:
    config_path = LuckConstant.get_config_path()
    with open(config_path, "r", encoding="utf-8") as fp:
        json_str = fp.read().strip()
        if json_str is not None and len(json_str) > 0:
            config = json.loads(json_str)
            # 认证鉴权
            basic_auth1 = config["basic_auth1"]
            if basic_auth1 is None or len(basic_auth1) <= 0:
                basic_auth1 = "admin"
            config["basic_auth1"] = basic_auth1

            basic_auth2 = config["basic_auth2"]
            if basic_auth2 is None or len(basic_auth2) <= 0:
                basic_auth2 = "luckserver"
            config["basic_auth2"] = basic_auth2

# 基本认证鉴权
app.config['BASIC_AUTH_USERNAME'] = config["basic_auth1"]
app.config['BASIC_AUTH_PASSWORD'] = config["basic_auth2"]
app.config['BASIC_AUTH_FORCE'] = False  # 为True 就默认对所有 api 进行认证， 如果设置为 False， 那么可以通过 @basic_auth.required 对指定 api 进行认证。
basic_auth = BasicAuth(app)

# 将配置数据存储在应用程序的配置对象中
app.config['CONFIG_DATA'] = config

# 初始化数据库
logger.info("开始初始化数据库...")
if LuckDbInitHelperUtils.init_database():
    logger.info("数据库初始化成功")
else:
    logger.error("数据库初始化失败")

# 注册地图模块蓝图
app.register_blueprint(baidu_map_bp)
app.register_blueprint(gaode_map_bp)
# 注册费用计算模块蓝图
app.register_blueprint(cost_bp)

IGNORE_AUTH_URL = [
    "/",

    "/login",
    "/logout",
    "/map/sdk/baidu/mapSdkBaidu/noGetwayReadRoute",
    "/map/sdk/gaode/mapSdkGaode/noGetwayReadRoute",
    "/map/cost/mapCost/noGetwayReadCost",
    "/gaode_map.html",
    "/baidu_map.html"
]

# 中间件：记录接口请求日志
@app.before_request
def log_before_request():
    if request.path == "/favicon.ico":
        return None
    """
    if request.method != 'OPTIONS':
        # 添加访问日志
        log = LuckLogHelperUtils(config)
        log.add_visit_log({
            "requri": request.path,
            "reqtype": request.method,
            "reqmethod": request.method,
            "reqparam": "",
            "reqnote": ""
        })
    """
    # 检查是否为静态文件请求
    if request.path.startswith('/static/'):
        return None
        
    if request.method != 'OPTIONS' and request.path not in IGNORE_AUTH_URL:
        # 从请求头部获取access_token
        access_token = request.headers.get(LuckConstant.ACCESS_TOKEN)
        if not access_token:
            # 将字节串解码为字符串
            query_string = request.query_string
            if query_string:
                query_string = query_string.decode('utf-8')
                # 解析查询字符串为字典
                params = parse_qs(query_string)
                authorization = params.get(LuckConstant.ACCESS_TOKEN)
                if authorization:
                    access_token = authorization[0]
        print(LuckConstant.ACCESS_TOKEN, access_token)
        # 使用新的JWT token验证逻辑
        if access_token is not None:
            """
            全局拦截：存在英文单引号就进行转义处理
            """
            # 处理 request.args 中的参数
            for key, value in request.args.items():
                if "'" in value:
                    escaped_value = value.replace("'", "''")
                    request.args[key] = escaped_value

            # 处理 request.data 中的参数
            try:
                data = json.loads(request.data)
                for key, value in data.items():
                    if isinstance(value, str) and "'" in value:
                        data[key] = value.replace("'", "''")
                # 注意：不能直接修改request.data，这里只是处理数据
                # 如果需要使用修改后的数据，应该在后续处理中使用data变量
            except json.JSONDecodeError:
                pass
        else:
            # 如果没有提供 access_token 或验证失败，返回错误信息
            return jsonify({
                "code": LuckConstant.CODE_NO_AUTH,
                "msg": LuckConstant.MSG_NO_AUTH
            }), LuckConstant.CODE_NO_AUTH

# 错误处理器：记录异常信息
@app.errorhandler(Exception)
def log_exception(error):
    if request.method != 'OPTIONS':
        # 添加错误日志
        log = LuckLogHelperUtils(config)
        """
        log.add_error_log({
            "requri": request.path,
            "reqtype": request.mimetype,
            "reqmethod": request.method,
            "reqparam": "",
            "reqnote": "",
            "reqerror": str(error)
        })
        """
        response = jsonify({
                "code": LuckConstant.CODE_NO_AUTH,
                "msg": "服务内部错误"+str(error)
            })
        response.status_code = 500
        return response
    # 对于OPTIONS请求，返回空响应
    return ""

# 收藏夹图标
@app.route('/favicon.ico')
def favicon():
    try:
        script_dir = os.path.dirname(os.path.abspath(__file__))  # 项目工程路径/luck
        resource = os.path.join(script_dir, 'resource')
        return send_from_directory(resource, 'favicon.ico', mimetype='image/vnd.microsoft.icon')
    except Exception as e:
        return "Error retrieving favicon: " + str(e), 500

# 移除收藏夹图标
@app.after_request
def remove_favicon(response):
    if request.path == '/favicon.ico':
        return Response()
    return response

# 检查MQ服务状态
@app.route('/')
#@basic_auth.required
def index():
    print(f'小程序 于 {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} 查看短信服务部署状态')

    return {
        "code": LuckConstant.CODE_SUCCESS,
        "data": [
            "/",
            "/sms/auth",
            "/sms/send",
            "/sms/verification"
        ],
        "msg": "短信服务部署成功"
    }

def main():
    app.run(debug=False, host='0.0.0.0', port=config["server_port"])

if __name__ == '__main__':
    main()